<?php
// $Id: brazilianids_cck.inc,v 1.2.2.5 2010/01/12 00:34:01 wasare Exp $
/**
 * @file
 * brazilianids_cck.inc
 * Deal with brazilian IDs like CPF and CNPJ as CCK fields.
 */

// ========================================== //
// DEFINING THE FIELDS
// ========================================== //

/**
 * Implementation of hook_field_info().
 */
function brazilianids_field_info() {
  return array(
    'brazilianids_cpf'     => array(
      'label' => t('CPF'),
    ),
    'brazilianids_cnpj'    => array(
      'label' => t('CNPJ'),
    ),
    'brazilianids_cpfcnpj' => array(
      'label' => t('CPF / CNPJ'),
    ),
  );
}

/**
 * Implementation of hook_field_settings().
 */
function brazilianids_field_settings($op, $field) {
  switch ($op) {
    case 'database columns':
      $columns['value'] = array(
        'type' => 'varchar',
        'length' => 18,
        'not null' => FALSE,
        'sortable' => TRUE,
        'views' => TRUE,
      );
      return $columns;
  }
}

/**
 * Implementation of hook_field().
 */
function brazilianids_field($op, &$node, $field, &$items, $teaser, $page) {
  switch ($op) {
    case 'validate':
      if (is_array($items)) {
        module_load_include('check.inc', 'brazilianids', 'brazilianids');
        foreach ($items as $delta => $item) {
          $error_element = isset($item['_error_element']) ? $item['_error_element'] : '';
          if (is_array($item) && isset($item['_error_element'])) unset($item['_error_element']);
          if (!empty($item['value'])) {
            // Validate the CPF, if any
            $error_element = isset($item['_error_element']) ? $item['_error_element'] : '';
            if ($field['type'] == 'brazilianids_cpf' && !_brazilianids_validate_cpf($item['value'])) {
              form_set_error($field['field_name'], t('"%value" is not a valid CPF', array('%value' => $item['value'])));
            }
            // Validate the CNPJ, if any
            if ($field['type'] == 'brazilianids_cnpj' && !_brazilianids_validate_cnpj($item['value'])) {
              form_set_error($field['field_name'], t('"%value" is not a valid CNPJ', array('%value' => $item['value'])));
            }
            //  Validate the CPF OR CNPJ, if any
            if ($field['type'] == 'brazilianids_cpfcnpj' && !_brazilianids_validate_cpf($item['value']) &&  !_brazilianids_validate_cnpj($item['value'])) {
              form_set_error($field['field_name'], t('"%value" is not a valid CPF neither CNPJ', array('%value' => $item['value'])));
            }
          }
        }
      }
      return $items;
    case 'sanitize':
      if (is_array($items)) {
        foreach ($items as $delta => $item) {
          $brazilianids = check_plain($item['value']);
          $items[$delta]['safe'] = $brazilianids;
        }
      }
    case 'view':
      if (is_array($items)) {
        // Include the validation/format functions
        module_load_include('check.inc', 'brazilianids', 'brazilianids');
        foreach ($items as $delta => $item) {
          if (is_array($item) && !empty($item['value'])) {
            // Format CPF OR CNPJ, if any
            $items[$delta]['view'] = _brazilianids_is_cpf_cnpj($item['value']) == 'CPF' ? _brazilianids_format_cpf($item['value']) : _brazilianids_format_cnpj($item['value']);
          }
        }
      }
  }
}

/**
 * Implementation of hook_content_is_empty().
 */
function brazilianids_content_is_empty($item, $field) {
  if (empty($item['value'])) {
    return TRUE;
  }
  return FALSE;
}


// ========================================== //
// DEFINING FORMATTERS
// ========================================== //

/**
 * Implementation of hook_theme().
 */
function brazilianids_theme() {
  return array(
    // Themes for the formatters.
    'brazilianids_formatter_default' => array(
      'arguments' => array('element' => NULL),
    ),
    'brazilianids_formatter_plain' => array(
      'arguments' => array('element' => NULL),
    ),
  );
}

/**
 * Implementation of hook_field_formatter_info().
 */
function brazilianids_field_formatter_info() {
  return array(
    'default' => array(
      'label' => t('Default'),
      'field types' => array(
        'brazilianids_cpf',
        'brazilianids_cnpj',
        'brazilianids_cpfcnpj'),
      'multiple values' => CONTENT_HANDLE_CORE,
    ),
    'plain' => array(
      'label' => t('Plain text'),
      'field types' => array(
        'brazilianids_cpf',
        'brazilianids_cnpj',
        'brazilianids_cpfcnpj'),
      'multiple values' => CONTENT_HANDLE_CORE,
    ),
  );
}

/**
 * Theme function for 'default' brazilianids field formatter.
 *
 * $element['#item']: the sanitized $delta value for the item,
 * $element['#field_name']: the field name,
 * $element['#type_name']: the $node->type,
 * $element['#formatter']: the $formatter_name,
 * $element['#node']: the $node,
 * $element['#delta']: the delta of this item, like '0',
 */
function theme_brazilianids_formatter_default($element) {
  return !empty($element['#item']['view']) ? $element['#item']['view'] : strip_tags($element['#item']['safe']);
}

/**
 * Theme function for 'plain' brazilianids field formatter.
 */
function theme_brazilianids_formatter_plain($element) {
  return !empty($element['#item']['safe']) ? strip_tags($element['#item']['safe']) : '';
}


// ========================================== //
// DEFINING A WIDGET
// ========================================== //

/**
 * Implementation of hook_widget_info().
 */
function brazilianids_widget_info() {
  return array(
    'brazilianids_cpf_cnpj'      => array(
      'label'       => t('Textfield'),
      'field types' => array(
        'brazilianids_cpf',
        'brazilianids_cnpj',
        'brazilianids_cpfcnpj'),
      // Who will handle multiple values, default is core.
      // 'CONTENT_HANDLE_MODULE' means the module does it.
      // See optionwidgets for an becape_cck of a module that
      // handles its own multiple values.
      'multiple values' => CONTENT_HANDLE_CORE,
      'callbacks' => array(
        // CONTENT_CALLBACK_DEFAULT default value, default is core.
        // 'CONTENT_CALLBACK_CUSTOM' means the module does it.
        // 'CONTENT_CALLBACK_NONE' means this widget has
        // no default value.
        'default value' => CONTENT_CALLBACK_DEFAULT,
      ),
    ),
  );
}

/**
 * Implementation of hook_widget_settings().
 */
function brazilianids_widget_settings($op, $widget) {
  switch ($op) {
    // Create the form element to be used on the widget
    // settings form. Widget settings can be different
    // for each shared instance of the same field and
    // should define the way the value is displayed to
    // the user in the edit form for that content type.
    case 'form':
      $form = array();
      $size = (isset($widget['size']) && is_numeric($widget['size'])) ? $widget['size'] : 18;
      $form['size'] = array(
        '#type' => 'textfield',
        '#title' => t('Size of textfield'),
        '#default_value' => $size,
        '#element_validate' => array('_element_validate_integer_positive'),
        '#required' => TRUE,
      );
      return $form;

    // Return an array of the names of the widget settings
    // defined by this module. These are the items that
    // CCK will store in the widget definition and they
    // will be available in the $field['widget'] array.
    // This should match the items defined in 'form' above.
    case 'save':
      return array('size');
  }
}

/**
 * Implementation of hook_widget().
 *
 * Attach a single form element to the form.
 *
 * CCK core fields only add a stub element and builds
 * the complete item in #process so reusable elements
 * created by hook_elements can be plugged into any
 * module that provides valid $field information.
 *
 * Custom widgets that don't care about using hook_elements
 * can be built out completely at this time.
 *
 * If there are multiple values for this field and CCK is
 * handling multiple values, the content module will call
 * this function as many times as needed.
 *
 * @param $form
 *   the entire form array,
 *   $form['#node'] holds node information
 * @param $form_state
 *   the form_state,
 *   $form_state['values'][$field['field_name']]
 *   holds the field's form values.
 * @param $field
 *   the field array
 * @param $items
 *   array of default values for this field
 * @param $delta
 *   the order of this item in the array of
 *   subelements (0, 1, 2, etc)
 *
 * @return
 *   the form item for a single element for this field
 */
function brazilianids_widget(&$form, &$form_state, $field, $items, $delta = 0) {

  $element['value'] = array(
    '#type' => 'textfield',
    '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL,
    '#title'         => $field['widget']['label'],
    '#required'      => $field['required'] ? $field['required'] : FALSE,
    '#autocomplete_path' => $element['#autocomplete_path'],
    '#size' => !empty($field['widget']['size']) ? $field['widget']['size'] : 18,
    '#attributes' => array('class' => 'edit-brazilianids'),
    '#maxlength' => $field['type'] == 'brazilianids_cpf' ? 14 : 18,
    '#description'   => $field['widget']['description'],
  );

  // Used so that hook_field('validate') knows where to
  // flag an error in deeply nested forms.
  if (empty($form['#parents'])) {
    $form['#parents'] = array();
  }
  $element['_error_element'] = array(
    '#type' => 'value',
    '#value' => implode('][', array_merge($form['#parents'], array('value'))),
  );

  return $element;
}